﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace ECANPort
{

    /// <summary>
    /// 设备类型 -但是实际使用时给的是1
    /// </summary>
    public enum DeviceType
    {
        USBCAN_I = 3,
        USBCAN_II = 4,
    }


    [Flags]
    public enum ECANStatus : uint
    {
        /// <summary>
        ///  error
        /// </summary>
        STATUS_ERR = 0,
        /// <summary>
        /// No error
        /// </summary>
        STATUS_OK = 1,
    }


    public enum E_SendType : byte
    {
        正常发送,
        单次发送,
        自发自收,
        单次自发自收
    }
    public enum E_RemoteFlag : byte
    {
        数据帧,
        远程帧,
    }
    /// <summary>
    /// CAN帧的数据结构
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct CAN_OBJ
    {
        /// <summary>
        /// 报文帧ID
        /// </summary>
        public uint ID { get; set; }
        /// <summary>
        /// 接收到信息帧时的时间标识 us
        /// </summary>
        public uint TimeStamp { get; set; }
        /// <summary>
        /// 是否使用时间标识，为1时TimeStamp有效，TimeFlag和TimeStamp只在此帧为接收帧时有意义
        /// </summary>
        public byte TimeFlag { get; set; }
        /// <summary>
        /// 发送帧类型
        /// =0时为正常发送，
        /// =1时为单次发送（不自动重发），
        /// =2时 为自发自收（用于测试CAN卡是否损坏），
        /// =3时为单次自发自收（只发送一 次，用于自测试），
        /// 只在此帧为发送帧时有意义。
        /// </summary>
        public byte SendType { get; set; }
        /// <summary>
        /// 是否是远程帧。=0时为数据帧，=1时为远程帧
        /// </summary>
        public byte RemoteFlag { get; set; }
        /// <summary>
        /// 是否是扩展帧。=0时为标准帧（11位帧ID），=1时为扩展帧（29位帧ID）。
        /// </summary>
        public byte ExternFlag { get; set; }
        /// <summary>
        /// 数据长度DLC(<=8)，即Data的长度
        /// </summary>
        public byte DataLen { get; set; }
        /// <summary>
        /// CAN报文的数据。空间受DataLen的约束
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] data;



        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        public byte[] Reserved;

        public override string ToString()
        {
            string timestr = "";
            if (TimeFlag != 0)
            {
                timestr = string.Format("{0:X8}h", TimeStamp);
            }

            string idstr = "";
            if (ExternFlag == 1)
                idstr = string.Format("{0:X8} ", ID);
            else
                idstr = string.Format("{0:X3} ", ID);

            string ExternStr = "Exten";
            if (ExternFlag == 0)
            {
                ExternStr = "Stand";
            }

            string typestr = "Romte";
            if (RemoteFlag == 0)
            {
                typestr = "Data";
            }

            string rt;
            if (RemoteFlag == 0)
            {
                string datastr = "";
                if (DataLen > 8)
                {
                    DataLen = 8;
                }
                for (int j = 0; j < DataLen; j++)
                {
                    datastr += string.Format("{0:X2} ", data[j]);
                }
                rt = $"Time:{timestr} ID:{idstr} FrameFormat:{ExternStr} Type:{typestr} Data:{datastr}";
            }
            else
            {
                rt = $"Time:{timestr} ID:{idstr} FrameFormat:{ExternStr} Type:{typestr} Remote";
            }
            return rt;
        }
    }

    /// <summary>
    /// 错误码
    /// </summary>
    enum E_ErrCode : uint
    {
        /// <summary>
        /// CAN控制器内部FIFO溢出
        /// </summary>
        ERR_CAN_OVERFLOW = 0x01,
        /// <summary>
        /// CAN控制器错误报警
        /// </summary>
        ERR_CAN_ERRALARM = 0x02,
        /// <summary>
        /// CAN控制器消极错误 -Passive_ErrData
        /// </summary>
        ERR_CAN_PASSIVE = 0x04,
        /// <summary>
        /// CAN控制器仲裁丢失  -ArLost_ErrData
        /// </summary>
        ERR_CAN_LOSE = 0x08,
        /// <summary>
        /// CAN控制器总线错误
        /// </summary>
        ERR_CAN_BUSERR = 0x10,
        /// <summary>
        /// CAN接收寄存器满
        /// </summary>
        ERR_CAN_REG_FULL = 0x20,
        /// <summary>
        /// CAN接收寄存器溢出
        /// </summary>
        ERR_CAN_REC_OVER = 0x40,
        /// <summary>
        /// CAN控制器主动错误
        /// </summary>
        ERR_CAN_ACTIVE = 0x80,
        /// <summary>
        /// 设备已经打开
        /// </summary>
        ERR_DEVICEOPENED = 0x100,
        /// <summary>
        /// 打开设备错误
        /// </summary>
        ERR_DEVICEOPEN = 0x200,
        /// <summary>
        /// 设备没有打开
        /// </summary>
        ERR_DEVICENOTOPEN = 0x300,
        /// <summary>
        /// 缓冲区溢出
        /// </summary>
        ERR_BUFFEROVERFLOW = 0x800,
        /// <summary>
        /// 此设备不存在
        /// </summary>
        ERR_DEVICENOTEXIST = 0x1000,
        /// <summary>
        /// 装载动态库失败
        /// </summary>
        ERR_LOADKERNELDLL = 0x2000,
        /// <summary>
        /// 执行命令失败错误码
        /// </summary>
        ERR_CMDFAILED = 0x4000,
        /// <summary>
        /// 内存不足
        /// </summary>
        ERR_BUFFERCREATE = 0x8000,
    }

    /// <summary>
    /// 消极错误数据0
    /// </summary>
    public struct Passive_ErrData0
    {
        /// <summary>
        /// 错误代码类型 6 7Bit
        ///  0：位错
        ///  1：格式错
        ///  2：填充错
        ///  3：其它错误
        /// </summary>
        public byte PassiveErrType;
        /// <summary>
        /// 错误属性 5Bit 
        /// 0：发送时发生的错误
        /// 1：接收时发生的错误
        /// </summary>
        public byte PassiveErrProp;
        /// <summary>
        /// 错误段表示 0-4Bit
        /// 2：ID.28-ID.21
        /// 3：帧开始
        /// 4：SRTR位
        /// 5：IDE位
        /// 8：CRC序列
        /// 10：数据区
        /// 11：数据长度代码
        /// 12：RTR位
        /// 18：中止
        /// 26：帧结束
        /// 28：溢出标志
        /// </summary>
        public byte PassiveErrCode;

        public static implicit operator Passive_ErrData0(byte data)
        {
            Passive_ErrData0 rt = new Passive_ErrData0();
            rt.PassiveErrCode = (byte)(data & 0b_0001111);
            rt.PassiveErrProp = (byte)(data & 0b_0010000);
            rt.PassiveErrType = (byte)(data & 0b_1100000);
            return rt;
        }

        public static implicit operator byte(Passive_ErrData0 data)
        {
            byte rt = 0;
            rt = (byte)(data.PassiveErrCode & 0b_0001111);
            rt += (byte)(data.PassiveErrProp & 0b_0010000);
            rt += (byte)(data.PassiveErrType & 0b_1100000);
            return rt;
        }

    }

    /// <summary>
    /// 低5位 仲裁丢失错误
    /// </summary>
    public enum ArLost
    {
        /// <summary>
        /// 仲裁丢失在识别码的bit1
        /// </summary>
        ArLost_Bit1 = 0,
        /// <summary>
        /// 仲裁丢失在识别码的bit2
        /// </summary>
        ArLost_Bit2 = 1,
        ArLost_Bit3 = 2,
        ArLost_Bit4 = 3,
        ArLost_Bit5 = 4,
        ArLost_Bit6 = 5,
        ArLost_Bit7 = 6,
        ArLost_Bit8 = 7,
        ArLost_Bit9 = 8,
        ArLost_Bit10 = 9,
        /// <summary>
        /// 仲裁丢失在识别码的bit11
        /// </summary>
        ArLost_Bit11 = 10,
        /// <summary>
        /// 仲裁丢失在SRTR位
        /// </summary>
        ArLost_SRTR = 11,
        /// <summary>
        /// 仲裁丢失在IDE位
        /// </summary>
        ArLost_IDE = 12,
        /// <summary>
        /// 仲裁丢失在识别码的bit12
        /// </summary>
        ArLost_Bit12 = 13,
        ArLost_Bit13 = 14,
        ArLost_Bit14 = 15,
        ArLost_Bit15 = 16,
        ArLost_Bit16 = 17,
        ArLost_Bit17 = 18,
        ArLost_Bit18 = 19,
        ArLost_Bit19 = 20,
        ArLost_Bit20 = 21,
        ArLost_Bit21 = 22,
        ArLost_Bit22 = 23,
        ArLost_Bit23 = 24,
        ArLost_Bit24 = 25,
        ArLost_Bit25 = 26,
        ArLost_Bit26 = 27,
        ArLost_Bit27 = 28,
        ArLost_Bit28 = 29,
        /// <summary>
        /// 仲裁丢失在识别码的bit29
        /// </summary>
        ArLost_Bit29 = 30,
        /// <summary>
        /// 仲裁丢失在ERTR位
        /// </summary>
        ArLost_ERTR = 31,

    }
    public struct CAN_ERR_INFO
    {
        /// <summary>
        /// 错误码 E_ErrCode
        /// </summary>
        public uint ErrCode;
        /// <summary>
        /// 当产生的错误中有消极错误时表示为消极错误的错误标识数据
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        public byte[] Passive_ErrData;
        /// <summary>
        /// 当产生的错误中有仲裁丢失错误时表示为仲裁丢失错误的错误标识数据。
        /// </summary>
        public byte ArLost_ErrData;

        public override string ToString()
        {
            string rtstr = (string.Format("{0:F} ", (E_ErrCode)ErrCode));
            rtstr += (string.Format("{0:X4}h", Passive_ErrData[1]));
            rtstr += (string.Format("{0:X4}h", Passive_ErrData[2]));
            rtstr += (string.Format("{0:X2}h", ArLost_ErrData));
            return rtstr;
        }

        public string ToStringEnum()
        {
            E_ErrCode eec = (E_ErrCode)ErrCode;
            ArLost ealost = (ArLost)ArLost_ErrData;

            string rtstr = (string.Format("{0:F}", eec));
            Passive_ErrData0 d0 = Passive_ErrData[0];

            rtstr += (string.Format("{0:X4}h", Passive_ErrData[1]));
            rtstr += (string.Format("{0:X4}h", Passive_ErrData[2]));
            rtstr += (string.Format("{0:F}", ealost));
            return rtstr;
        }

    }


    public enum CanMode : byte
    {
        NormalMode = 0,
        ListionModel = 1,
        SelfTestMdoel = 2
    }

    [Serializable]
    /// <summary>
    /// CAN的初始化配置
    /// </summary>
    public struct INIT_CONFIG
    {
        /// <summary>
        /// SJA1000的帧过滤验收码
        /// </summary>
        public uint AccCode { get; set; }
        /// <summary>
        /// 屏蔽码
        /// SJA1000的帧过滤屏蔽码。屏蔽码推荐设置为0xFFFF FFFF，即全部接收。
        /// </summary>
        public uint AccMask { get; set; }
        public uint Reserved { get; set; }

        /// <summary>
        /// 滤波使能。0=不使能，1=使能。使能时，请参照SJA1000验收滤波器设置 验收码和屏蔽码
        /// </summary>
        public byte Filter { get; set; }
        /// <summary>
        /// 波特率定时器0
        /// </summary>
        public byte Timing0 { get; set; }
        /// <summary>
        /// 波特率定时器1
        /// </summary>
        public byte Timing1 { get; set; }
        /// <summary>
        /// 模式 =0为正常模式，=1为只听模式，=2为自发自收模式
        /// </summary>
        public byte Mode { get; set; }

        public static void SetBaudrate(CanBaud nK, ref INIT_CONFIG init_config)
        {
            switch (nK)
            {
                case CanBaud.B_1000K: //1000  0
                    init_config.Timing0 = 0;
                    init_config.Timing1 = 0x14;//20
                    break;
                case CanBaud.B_800K:
                    init_config.Timing0 = 0;
                    init_config.Timing1 = 0x16;//22
                    break;
                case CanBaud.B_666K:
                    init_config.Timing0 = 0x80;//128
                    init_config.Timing1 = 0xb6;//182
                    break;
                case CanBaud.B_500K: //500
                    init_config.Timing0 = 0;
                    init_config.Timing1 = 0x1c;//28
                    break;
                case CanBaud.B_400K://400
                    init_config.Timing0 = 0x80;
                    init_config.Timing1 = 0xfa;
                    break;
                case CanBaud.B_250K://250
                    init_config.Timing0 = 0x01;
                    init_config.Timing1 = 0x1c;
                    break;
                case CanBaud.B_200K://200
                    init_config.Timing0 = 0x81;
                    init_config.Timing1 = 0xfa;
                    break;
                case CanBaud.B_125K://125
                    init_config.Timing0 = 0x03;
                    init_config.Timing1 = 0x1c;
                    break;
                case CanBaud.B_100K://100
                    init_config.Timing0 = 0x04;
                    init_config.Timing1 = 0x1c;
                    break;
                case CanBaud.B_80K://80
                    init_config.Timing0 = 0x83;
                    init_config.Timing1 = 0xff;
                    break;
                case CanBaud.B_50K://50
                    init_config.Timing0 = 0x09;
                    init_config.Timing1 = 0x1c;
                    break;
                default:
                    break;
            }
        }

        public static (bool ok, CanBaud baud) GetCanBaud(byte Timing0, byte Timing1)
        {
            CanBaud baud = CanBaud.B_500K;
            INIT_CONFIG tmp = new INIT_CONFIG();
            int C = Enum.GetNames(typeof(CanBaud)).Length;
            for (int i = 0; i < C; i++)
            {
                INIT_CONFIG.SetBaudrate((CanBaud)i, ref tmp);
                if (tmp.Timing0 == Timing0 && tmp.Timing1 == Timing1)
                {
                    baud = (CanBaud)i;
                    return (true, baud);
                }
            }

            return (false, baud);
        }

        public override string ToString()
        {
            string rt = "";
            rt += "AccCode:" + AccCode;
            rt += " AccMask:" + AccMask;
            rt += " Filter:" + Filter;
            rt += " Timing0:" + Timing0;
            rt += " Timing1:" + Timing1;
            rt += " Mode:" + Mode;
            return rt;
        }
    }

    /// <summary>
    /// ECAN系列接口卡的设备信息
    /// 结构体将在ReadBoardInfo函数中被填充
    /// </summary>
    public struct BOARD_INFO
    {
        /// <summary>
        /// 硬件版本号，用16进制表示
        /// </summary>
        public ushort hw_Version;
        /// <summary>
        /// 固件版本号
        /// </summary>
        public ushort fw_Version;
        /// <summary>
        /// 驱动程序版本号
        /// </summary>
        public ushort dr_Version;
        /// <summary>
        /// 接口库版本号
        /// </summary>
        public ushort in_Version;
        /// <summary>
        /// 板卡所使用的中断号
        /// </summary>
        public ushort irq_Num;
        /// <summary>
        /// CAN通道数量
        /// </summary>
        public byte can_Num;
        /// <summary>
        /// 板卡的序列号。
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
        public byte[] str_Serial_Num;
        /// <summary>
        /// 硬件类型
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
        public byte[] str_hw_Type;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public ushort[] Reserved;
    }

    /// <summary>
    /// 含CAN控制器状态信息
    /// 在ReadCanStatus 函数中被填充
    /// </summary>
    public struct CAN_STATUS
    {
        /// <summary>
        /// 中断记录，读操作会清除
        /// </summary>
        public byte ErrInterrupt;
        /// <summary>
        /// CAN控制器模式寄存器
        /// </summary>
        public byte regMode;
        /// <summary>
        /// CAN控制器状态寄存器
        /// </summary>
        public byte regStatus;
        /// <summary>
        /// CAN控制器仲裁丢失寄存器
        /// </summary>
        public byte regALCapture;
        /// <summary>
        /// CAN控制器错误寄存器
        /// </summary>
        public byte regECCapture;
        /// <summary>
        /// CAN控制器错误警告限制寄存器
        /// </summary>
        public byte regEWLimit;
        /// <summary>
        /// CAN控制器接收错误寄存器
        /// </summary>
        public byte regRECounter;
        /// <summary>
        /// CAN控制器发送错误寄存器
        /// </summary>
        public byte regTECounter;
        public int Reserved;
    }


    /// <summary>
    /// CAN滤波器的滤波范围  [未测试]
    /// 在SetReference函数中被填充
    /// </summary>
    public struct FILTER_RECORD
    {
        /// <summary>
        /// 过滤的帧类型标志，为1代表要过滤的为扩展帧，为0代表要过滤的为标 准帧
        /// </summary>
        public int ExtFrame;
        /// <summary>
        /// 滤波范围的起始帧ID
        /// </summary>
        public int Start;
        /// <summary>
        /// 滤波范围的结束帧ID
        /// </summary>
        public int End;
    }


    /// <summary>
    /// ECAN DLL API
    /// 流程:OpenDevice ->InitCAN->StartCAN->Transmit/Receive->CloseDevice
    /// </summary>
    public static class ECANDLL
    {
        public const string ECANDLL_NAME = "ECANVCI.dll";
        public static bool ECANDLL_LoadOk = false;
        /// <summary>
        /// 打开设备
        /// </summary>
        /// <param name="DeviceType">USBCAN I选择3,USBCAN II选择4</param>
        /// <param name="DeviceInd">设备索引号
        /// 当只有一个设备时,索引号为0,
        /// 有两个时,可以为0或1</param>
        /// <param name="Reserved"></param>
        /// <returns></returns>
        [DllImport(ECANDLL_NAME, EntryPoint = "OpenDevice")]
        public static extern ECANStatus OpenDevice(
            UInt32 DeviceType,
            UInt32 DeviceInd,
            UInt32 Reserved);

        /// <summary>
        /// 关闭设备
        /// </summary>
        /// <param name="DeviceType">设备类型号</param>
        /// <param name="DeviceInd">设备索引号</param>
        /// <returns></returns>
        [DllImport(ECANDLL_NAME, EntryPoint = "CloseDevice")]
        public static extern ECANStatus CloseDevice(
            UInt32 DeviceType,
            UInt32 DeviceInd);

        /// <summary>
        /// 初始化指定的CAN通道
        /// </summary>
        /// <param name="DeviceType">设备类型号</param>
        /// <param name="DeviceInd">设备索引号</param>
        /// <param name="CANInd">第几路CAN:CAN0为0，CAN1为1</param>
        /// <param name="InitConfig">初始化参数结构</param>
        /// <returns></returns>
        [DllImport(ECANDLL_NAME, EntryPoint = "InitCAN")]
        public static extern ECANStatus InitCAN(
            UInt32 DeviceType,
            UInt32 DeviceInd,
            UInt32 CANInd,
            ref INIT_CONFIG InitConfig);

        /// <summary>
        /// 启动USBCAN设备的某一个CAN通道
        /// 多个CAN通道时，需要 多次调用
        /// </summary>
        /// <param name="DeviceType">设备类型号</param>
        /// <param name="DeviceInd">设备索引号</param>
        /// <param name="CANInd">第几路CAN</param>
        /// <returns></returns>
        [DllImport(ECANDLL_NAME, EntryPoint = "StartCAN")]
        public static extern ECANStatus StartCAN(
            UInt32 DeviceType,
            UInt32 DeviceInd,
            UInt32 CANInd);


        [DllImport(ECANDLL_NAME, EntryPoint = "ResetCAN")]
        public static extern ECANStatus ResetCAN(
            UInt32 DeviceType,
            UInt32 DeviceInd,
            UInt32 CANInd);


        [DllImport(ECANDLL_NAME, EntryPoint = "Transmit")]
        public static extern ECANStatus Transmit(
            UInt32 DeviceType,
            UInt32 DeviceInd,
            UInt32 CANInd,
            ref CAN_OBJ Send,
            UInt16 length);


        [DllImport(ECANDLL_NAME, EntryPoint = "Receive")]
        public static extern ECANStatus Receive(
            UInt32 DeviceType,
            UInt32 DeviceInd,
            UInt32 CANInd,
            out CAN_OBJ Receive,
            UInt32 length,
            UInt32 WaitTime);

        [DllImport(ECANDLL_NAME, EntryPoint = "ReadErrInfo")]
        public static extern ECANStatus ReadErrInfo(
            UInt32 DeviceType,
            UInt32 DeviceInd,
            UInt32 CANInd,
            out CAN_ERR_INFO ReadErrInfo);


        /// <summary>
        /// 获取设备信息
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="ReadErrInfo"></param>
        /// <returns></returns>
        [DllImport(ECANDLL_NAME, EntryPoint = "ReadBoardInfo")]
        public static extern ECANStatus ReadBoardInfo(
            UInt32 DeviceType,
            UInt32 DeviceInd,
            out BOARD_INFO ReadErrInfo);

        /// <summary>
        /// 获取CAN状态 [未测试]
        /// </summary>
        /// <param name="DeviceType">设备类型号</param>
        /// <param name="DeviceInd">设备索引号</param>
        /// <param name="CANInd">第几路CAN</param>
        /// <param name="Can_Status">CAN状态</param>
        /// <returns></returns>
        [DllImport(ECANDLL_NAME, EntryPoint = "ReadCanStatus")]
        public static extern ECANStatus ReadCanStatus(
            UInt32 DeviceType,
            UInt32 DeviceInd,
            UInt32 CANInd,
            out CAN_STATUS Can_Status);

        /// <summary>
        /// 获取设备的相应参数 [未测试]
        /// </summary>
        /// <param name="DeviceType">设备类型号</param>
        /// <param name="DeviceInd">设备索引号</param>
        /// <param name="CANInd">第几路CAN</param>
        /// <param name="RefType">参数类型</param>
        /// <param name="pData">关数据缓冲区地址首指针</param>
        /// <example>
        /// BYTE info[14];
        /// info[0] = 1;
        ///ECANStatus st1 = GetReference(4, 0, 0, 1, info);
        /// </example>
        /// <returns></returns>
        [DllImport(ECANDLL_NAME, EntryPoint = "GetReference")]
        public static extern ECANStatus GetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, byte[] pData);
        /// <summary>
        /// 此函数用以设置设备的相应参数，主要处理不同设备的特定操作 [未测试]
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="RefType"></param>
        /// <param name="pData"></param>
        /// <returns></returns>
        [DllImport(ECANDLL_NAME, EntryPoint = "SetReference")]
        public static extern ECANStatus SetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, byte[] pData);
        /// <summary>
        /// 获取指定接收缓冲区中接收到但尚未被读取的帧数量 [未测试]
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <returns>返回尚未被读取的帧数</returns>
        [DllImport(ECANDLL_NAME, EntryPoint = "GetReceiveNum")]
        public static extern UInt32 GetReceiveNum(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);
        /// <summary>
        /// 清空指定CAN通道的缓冲区 [未测试]
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <returns></returns>
        [DllImport(ECANDLL_NAME, EntryPoint = "ClearBuffer")]
        public static extern ECANStatus ClearBuffer(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);


    }



}
